#include <stdio.h>
#include <stdarg.h>
#include "prot_tcp.h"

static int debug_tcp = 0;

#define TCP_LOG(fmt, ...)\
  do {        \
    if (debug_tcp)\
      printf("[%s-%d]"fmt, __func__, __LINE__, ##__VA_ARGS__);\
  } while(0)


bool tcp_rx(struct llbuf *ll)
{
  TCP_LOG("tcp_rx\n");
#if 0
  for (s = tp_allsocs; s; s = s->next) {
		if (s->nl_protocol_id == NL_PROTOCOL_IPV4) {
			if (s->hisport != 0 && tp->dstPort == s->myport 
				&& tp->srcPort == s->hisport &&
      (htonl (rqts->ipv4_src_addr) == s->his_ipv4_addr))
      {
      break;
      }
    } 
  }
  if (s == NULL) {
    for (s = tp_allsocs; s; s = s->next) {
			if (((s->hisport == 0) && (tp->dstPort == s->myport))
        && (htonl (rqts->ipv4_dst_addr) == s->my_ipv4_addr)) {
        break;
			}
		}
	}
	if ((s) && (s->Initialized == 0)) {
    LOG ("Demuxed to an initialized socket %p %d\n", s, s->state);
    fflush (stdout);
	}
	if (s == NULL) {
    if (tp->flags & tp_FlagRST) {
      return;
    }
    if ((!(tp->flags & tp_FlagSYN)) || (tp->flags & tp_FlagACK)) {
      reset_now = 1;
    }
    if ((sys_memory.clust_in_use  + 20  > sys_memory.fclist.max_size) || (reset_now) ) {
      LOG ("Gateway: Connection being reset %d %d %d\n", 20, sys_memory.clust_promised, sys_memory.fclist.max_size);
      cluster_check = 0;
      if ((s1 = scps_socket (AF_INET, SOCK_STREAM, 0)) == -1) {
        cluster_check = 1;
        return;
      }
      s = (tp_Socket *) scheduler.sockets[s1].ptr;
      s->ph.nl_head.ipv4.src = htonl (rqts->ipv4_dst_addr);
      s->ph.nl_head.ipv4.dst = htonl (rqts->ipv4_src_addr);
      memset ((char *) &dst, 0, sizeof (dst));
      dst.sin_family = AF_INET;
      dst.sin_addr.s_addr = htonl (rqts->ipv4_dst_addr);
      dst.sin_port = tp->dstPort;
      if ((rc = scps_bind (s1, (struct sockaddr *) &dst, sizeof (dst))) == -1) {
        scps_close (s1);
        cluster_check = 1;
        return;
      }

      memcpy (&(s->his_ipv4_addr), &(rqts->ipv4_src_addr), sizeof (u_long));
      memcpy (&(s->hisport), &(tp->srcPort), sizeof (u_short));

      memcpy (&(s->src_mac_addr [0]), &(rqts->dst_mac_addr [0]),6);
      memcpy (&(s->dst_mac_addr [0]), &(rqts->src_mac_addr [0]),6);

      s->frame_type = rqts->frame_type;
      s->recv_tap_if = rqts->recv_tap_if;

      gateway_reset (s1, rqts, tp);
      cluster_check = 1;
      return;
    }

    memset ((char *) &dst, 0, sizeof (dst));
    dst.sin_family = AF_INET;
    dst.sin_addr.s_addr = htonl (rqts->ipv4_dst_addr);
    dst.sin_port = tp->dstPort;

    if ((s1 = scps_socket (AF_INET, SOCK_STREAM, 0)) == -1) {
      LOG ("Gateway: not allowing any more connection ss1\n");
      return;
    }

    s = (tp_Socket *) scheduler.sockets[s1].ptr;
    s->ph.nl_head.ipv4.src = htonl (rqts->ipv4_dst_addr);
    if ((rc = scps_bind (s1, (struct sockaddr *) &dst, sizeof (dst))) == -1) {
      LOG("Gateway: not allowing any more connection sb1\n");
      scps_close (s1);
      return;
    }

    memcpy (&(s->src_mac_addr [0]), &(rqts->dst_mac_addr [0]),6);
    memcpy (&(s->dst_mac_addr [0]), &(rqts->src_mac_addr [0]),6);
    s->frame_type = rqts->frame_type;
    s->recv_tap_if = rqts->recv_tap_if;

    s->DSCP = rqts->DSCP; 
    s->np_rqts.DSCP = rqts->DSCP;


    tp_mss (s, 0);    /* provisional mss setup */
    gateway_set_options (s->sockid, rqts->divert_port_number, 0);
    s->state_prev = tp_StateCLOSED;  /* Might have been tp_StateNASCENT */
    s->state = tp_StateLISTEN;

    s->timeout = 0x7ffffff;  /* forever... */
    s->hisport = 0;
    s->ph.nl_head.ipv4.dst = 0;
    s->link_outage = 0;  /* forced for now */

		/* Now create active socket */
    if (!reset_now) {
      int initial_port;
      memset ((char *) &src, 0, sizeof (src));
      src.sin_family = AF_INET;
      src.sin_addr.s_addr = htonl (rqts->ipv4_src_addr);
      initial_port = tp->srcPort;
      src.sin_port = initial_port;
      if ((s2 = scps_socket (AF_INET, SOCK_STREAM, 0)) == -1){
        LOG ("Gateway: not allowing any more connection ss2\n");
        reset_now = 1;
      } else {
        active_s = (tp_Socket *) scheduler.sockets[s2].ptr;
        active_s->ph.nl_head.ipv4.src = htonl (rqts->ipv4_src_addr);
        if ((rc = scps_bind (s2, (struct sockaddr *) &src, sizeof(src))) == -1) {
          LOG ("Gateway: not allowing any more connection sb2\n");
          close_s1 = 1;
          scps_close (active_s->sockid);
        }
        active_s->np_rqts.ipv4_src_addr = htonl (rqts->ipv4_src_addr);
        gateway_set_options (active_s->sockid, rqts->divert_port_number, 1);
        memcpy (&(active_s->src_mac_addr [0]), &(rqts->src_mac_addr [0]),6);
        memcpy (&(active_s->dst_mac_addr [0]), &(rqts->dst_mac_addr [0]),6);
        active_s->frame_type = rqts->frame_type;
        active_s->recv_tap_if = rqts->peer_tap_if;
        s->DSCP = rqts->DSCP; 
        active_s->DSCP = rqts->DSCP;
        active_s->np_rqts.DSCP = rqts->DSCP;
      }
    }

    /* Now connect the two sockets */
    if ((!reset_now) && (!close_s1)) {
      s->peer_socket = (tp_Socket *) active_s;
      s->gateway_flags |= GATEWAY_PEER_WIN_NOT_OPENED;
      active_s->peer_socket = (tp_Socket *) s;
      s->gateway_flags |= GATEWAY_SEND_SYN;
    } else {
      if (close_s1) {
        LOG ("Gateway: closing s1 - not enough resoures\n");
        scps_close (s1);
      }
      if (reset_now) {
        LOG ("Gateway: Reseting s1 - not enough resources\n");
        gateway_reset (s1, rqts, tp);
      }
      return;
    }

    /* Now do the accept which should just come naturally */
    gateway_double_check_parameters (s);
    gateway_double_check_parameters (active_s);
  } else {
    memcpy (&(s->np_rqts.src_mac_addr [0]), &(rqts->dst_mac_addr [0]),6);
    memcpy (&(s->np_rqts.dst_mac_addr [0]), &(rqts->src_mac_addr [0]),6);
  }

  if (odd_aligned){
    memcpy (&(s->in_th), ugly, tp_header_len);
    tp = (tp_Header *) & (s->in_th);
  } else {
    tp_header_len = tp->th_off << 2;
    data = ((byte *) tp + tp_header_len);
  }

  if (s->capabilities & CAP_COMPRESS)
    memcpy ((tp_Header *) & s->in_th, tp, ((tp->flags & tp_FlagDO) >> 10));
  len -= tp_header_len;
	
	if (s->rt_route) 
    s->rt_route->flags |= RT_LINK_AVAIL;

  if (s->state == tp_StateCLOSED){
    gateway_reset (s->sockid, rqts, tp);
    return;
  }

  if (s->DSCP != rqts ->DSCP) {
    s->DSCP = rqts->DSCP; 
  }

  if ((s->peer_socket) && (s->peer_socket->DSCP != rqts ->DSCP)) {
    s->peer_socket->DSCP = rqts->DSCP; 
  }

  rc = tp_CommonHandler (s, rqts, tp, data, len);
	  switch (rc)
    {
    case 0:
      scps_close (s1);
      scps_close (s2);
      break;

    case -2:
      tp_Abort (s1);
      tp_Abort (s2);
      break;

    case -1:
      break;

    default:
      if ((s->gateway_flags & GATEWAY_SEND_SYN)  && (s2 > 0)) {
        s->gateway_flags &= (~GATEWAY_SEND_SYN);
        SET_ERR (0);
        rc = tp_Connect (s2, (void *) &dst, sizeof (dst));
        if ((rc == -1) && (GET_ERR () != SCPS_EINPROGRESS)) {
          /* If you can complete the connection you must abort */
          tp_Abort (s1);
          tp_Abort (s2);
    
          return;
        }

        if ((s) && (active_s)) {
          route *tmp = active_s->peer_socket->rt_route;
 
          if (active_s->peer_socket->DSCP != rqts ->DSCP) {
              active_s->peer_socket->DSCP = rqts->DSCP; 
          }
          if (!(active_s->peer_socket->rt_route = route_rt_lookup_s (active_s->peer_socket))) {
            active_s->peer_socket->rt_route = tmp;
          }

          tmp = active_s->rt_route;
 
          if (active_s->DSCP != rqts ->DSCP) {
              active_s->DSCP = rqts->DSCP; 
          }
          if (!(active_s->rt_route = route_rt_lookup_s (active_s))) {
              active_s->rt_route = tmp;
        }
      }
    }
    break;
  }
#endif
  return false; 
}

bool tcp_tx(struct llbuf *ll)
{
  return false;
}

BOLT(debug_tcp, "debug tcp on")
{
  debug_tcp = 1;
}

BOLT(undebug_tcp, "debug tcp off")
{
  debug_tcp = 0;
}


BOLT_PROFILE(scps, "", scps_profile, desc)
{
  if (debug_tcp)
    cmd_load_profile(desc, "debug tcp on\n");
}
